/*
 * Copyright (c) 2024 Black Sesame Technologies
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cstdlib>
#include <gtest/gtest.h>
#include "dal/config_loader.h"
#include "dal/ret_type.h"
#include "dal/domain_participant.h"
#include "dal/publisher.h"
#include "dal_rticonnextdds/entity_impl/DalDataWriterImpl.hpp"
#include "dal_rticonnextdds/entity_impl/DalDataReaderImpl.hpp"
#include "dal_rticonnextdds/entity_impl/DalPublisherImpl.hpp"
#include "dal_rticonnextdds/entity_impl/DalSubscriberImpl.hpp"
#include "dal_rticonnextdds/entity_impl/DalTopicImpl.hpp"
#include "dal_rticonnextdds/entity_impl/DalDomainParticipantImpl.hpp"
#include "dal_rticonnextdds/config_manager/ConfigManager.hpp"

#include "ndds/ndds_c.h"

#include "mj_interfaces/msg/mjtype.hpp"

TEST(rti_qos_default, config_loader)
{
    DDS_DomainParticipantFactory* factory = DDS_DomainParticipantFactory_get_instance();
    DDS_DomainParticipantQos* r_part_qos = new DDS_DomainParticipantQos();
    DDS_DomainParticipantFactory_get_default_participant_qos(factory, r_part_qos);
    DDS_DomainParticipant* r_participant = DDS_DomainParticipantFactory_create_participant(
        factory, 23, r_part_qos, NULL, DDS_STATUS_MASK_NONE);

    DDS_PublisherQos* r_pub_qos = new DDS_PublisherQos();
    DDS_DomainParticipant_get_default_publisher_qos(r_participant, r_pub_qos);
    DDS_Publisher* r_pub = DDS_DomainParticipant_create_publisher(
        r_participant, r_pub_qos, NULL, DDS_STATUS_MASK_NONE);

    DDS_DataWriterQos* r_writer_qos = new DDS_DataWriterQos();
    DDS_Publisher_get_default_datawriter_qos(r_pub, r_writer_qos);

    EXPECT_EQ(r_writer_qos->durability.kind, DDS_VOLATILE_DURABILITY_QOS);
    EXPECT_EQ(r_writer_qos->reliability.kind, DDS_RELIABLE_RELIABILITY_QOS);
    EXPECT_EQ(r_writer_qos->history.kind, DDS_KEEP_LAST_HISTORY_QOS);
    EXPECT_EQ(r_writer_qos->liveliness.kind, DDS_AUTOMATIC_LIVELINESS_QOS);
}

TEST(config_loader_code_takes_effect, config_loader)
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    setenv("NDDS_USER_QOS_SET_FLAG", "0", 1);
    setenv("DAL_CONFIG_PATH", "./DAL_RTI_USER_QOS_PROFILES_wrong.xml", 1);
    ret = dal_load_config_from_path("./DAL_RTI_USER_QOS_PROFILES_wrong.xml", nullptr);
    EXPECT_EQ(ret, DAL_RETCODE_ERROR);

    const char* flag = getenv("NDDS_USER_QOS_SET_FLAG");
    EXPECT_STREQ(flag, "0");

    dal_domain_participant_t* participant =
        dal_create_domain_participant(20, DAL_PARTICIPANT_QOS_DEFAULT, NULL, DAL_ANY_STATUS);

    dal_publisher_t* pub = dal_domain_participant_create_publisher(
        participant, DAL_PUBLISHER_QOS_DEFAULT, NULL, DAL_ANY_STATUS);

    dal_type_support_t* ts =
        dal_create_type_support((void*)(mj_interfaces::msg::Mjtype::FullyQualifiedName()));
    ret = dal_type_support_register_type(ts, participant, dal_type_support_get_type_name(ts));

    dal_topic_t* topic = dal_domain_participant_create_topic(participant,
                                                             "HelloWorldTopic",
                                                             dal_type_support_get_type_name(ts),
                                                             DAL_TOPIC_QOS_DEFAULT,
                                                             NULL,
                                                             DAL_ANY_STATUS);

    dal_datawriter_qos_t* d_writer_qos = dal_create_default_datawriter_qos();
    EXPECT_EQ(d_writer_qos->durability.kind, DAL_TRANSIENT_LOCAL_DURABILITY_QOS);
    EXPECT_EQ(d_writer_qos->reliability.kind, DAL_RELIABLE_RELIABILITY_QOS);

    d_writer_qos->durability.kind = DAL_TRANSIENT_DURABILITY_QOS;
    d_writer_qos->reliability.kind = DAL_BEST_EFFORT_RELIABILITY_QOS;
    d_writer_qos->history.kind = DAL_KEEP_ALL_HISTORY_QOS;
    d_writer_qos->liveliness.kind = DAL_MANUAL_BY_TOPIC_LIVELINESS_QOS;

    dal_datawriter_t* writer =
        dal_publisher_create_datawriter(pub, topic, d_writer_qos, NULL, DAL_ANY_STATUS);
    EXPECT_NE(writer->handle_, nullptr);

    auto writer_impl = reinterpret_cast<DalDataWriterImpl*>(writer->handle_);
    auto r_writer = writer_impl->RtiddsDataWriter();
    DDS_DataWriterQos* r_qos = new DDS_DataWriterQos();
    EXPECT_EQ(DDS_DataWriter_get_qos(r_writer, r_qos), DDS_RETCODE_OK);

    EXPECT_EQ(r_qos->durability.kind, DDS_TRANSIENT_DURABILITY_QOS);
    EXPECT_EQ(r_qos->reliability.kind, DDS_BEST_EFFORT_RELIABILITY_QOS);
    EXPECT_EQ(r_qos->history.kind, DDS_KEEP_ALL_HISTORY_QOS);
    EXPECT_EQ(r_qos->liveliness.kind, DDS_MANUAL_BY_TOPIC_LIVELINESS_QOS);
}

TEST(rti_qos_by_profile, config_loader)
{
    DDS_ReturnCode_t retcode;
    DDS_DomainParticipantFactoryQos* r_factory_qos = new DDS_DomainParticipantFactoryQos();
    DDS_DomainParticipantFactory* factory = DDS_DomainParticipantFactory_get_instance();
    retcode = DDS_DomainParticipantFactory_get_qos(factory, r_factory_qos);
    EXPECT_EQ(retcode, DDS_RETCODE_OK);

    const char* url_profiles[1] = {"./DAL_RTI_USER_QOS_PROFILES.xml"};
    DDS_StringSeq_from_array(&r_factory_qos->profile.url_profile, url_profiles, 1);
    retcode = DDS_DomainParticipantFactory_set_qos(factory, r_factory_qos);
    EXPECT_EQ(retcode, DDS_RETCODE_OK);

    DDS_DomainParticipantQos* r_part_qos = new DDS_DomainParticipantQos();
    retcode = DDS_DomainParticipantFactory_get_participant_qos_from_profile(
        factory, r_part_qos, "dal_rticonnext_qos_profile_lib", "participant_qos_aaa");
    EXPECT_EQ(retcode, DDS_RETCODE_OK);

    EXPECT_STREQ(r_part_qos->participant_name.name, "participant_AAA");
    EXPECT_EQ(r_part_qos->discovery_config.builtin_discovery_plugins,
              DDS_DISCOVERYCONFIG_BUILTIN_SPDP);
    EXPECT_EQ(r_part_qos->discovery_config.participant_liveliness_lease_duration.sec, 2000);
    EXPECT_EQ(r_part_qos->discovery_config.participant_liveliness_assert_period.sec, 300);
    EXPECT_EQ(r_part_qos->discovery_config.initial_participant_announcements, 1);
    EXPECT_EQ(r_part_qos->discovery_config.min_initial_participant_announcement_period.sec, 1);
    EXPECT_EQ(r_part_qos->discovery_config.max_initial_participant_announcement_period.sec, 1);

    DDS_DomainParticipantQos* r_part_qos_2 = new DDS_DomainParticipantQos();
    retcode = DDS_DomainParticipantFactory_get_participant_qos_from_profile(
        factory, r_part_qos_2, "dal_rticonnext_qos_profile_lib", "participant_qos_bbb");
    EXPECT_EQ(retcode, DDS_RETCODE_OK);

    EXPECT_STREQ(r_part_qos_2->participant_name.name, "participant_BBB");
    EXPECT_EQ(r_part_qos_2->discovery_config.builtin_discovery_plugins,
              DDS_DISCOVERYCONFIG_BUILTIN_SPDP);
    EXPECT_EQ(r_part_qos_2->discovery_config.participant_liveliness_lease_duration.sec, 1000);
    EXPECT_EQ(r_part_qos_2->discovery_config.participant_liveliness_assert_period.sec, 500);
    EXPECT_EQ(r_part_qos_2->discovery_config.initial_participant_announcements, 2);
    EXPECT_EQ(r_part_qos_2->discovery_config.min_initial_participant_announcement_period.sec, 5);
    EXPECT_EQ(r_part_qos_2->discovery_config.max_initial_participant_announcement_period.sec, 5);
}

static void test_config_loader()
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    const char* flag = getenv("NDDS_USER_QOS_SET_FLAG");
    EXPECT_STREQ(flag, "1");

    // participant
    dal_domain_participant_t* participant =
        dal_create_domain_participant(20, DAL_PARTICIPANT_QOS_DEFAULT, NULL, DAL_ANY_STATUS);

    dal_domain_participant_t* participant_2 = dal_create_domain_participant_with_profile(
        20, DAL_PARTICIPANT_QOS_DEFAULT, "participant_qos_aaa", NULL, DAL_ANY_STATUS);

    auto part_impl = reinterpret_cast<DalDomainParticipantImpl*>(participant->handle_);
    auto f_part = part_impl->RtiddsDomainParticipant();
    auto part_impl_2 = reinterpret_cast<DalDomainParticipantImpl*>(participant_2->handle_);
    auto f_part_2 = part_impl_2->RtiddsDomainParticipant();

    DDS_DomainParticipantQos* f_part_qos = new DDS_DomainParticipantQos();
    DDS_DomainParticipantQos* f_part_qos_2 = new DDS_DomainParticipantQos();
    DDS_DomainParticipant_get_qos(f_part, f_part_qos);
    DDS_DomainParticipant_get_qos(f_part_2, f_part_qos_2);

    EXPECT_STRNE(f_part_qos->participant_name.name, "participant_AAA");

    EXPECT_STREQ(f_part_qos_2->participant_name.name, "participant_AAA");
    EXPECT_EQ(f_part_qos_2->discovery_config.builtin_discovery_plugins,
              DDS_DISCOVERYCONFIG_BUILTIN_SPDP);
    EXPECT_EQ(f_part_qos_2->discovery_config.participant_liveliness_lease_duration.sec, 2000);
    EXPECT_EQ(f_part_qos_2->discovery_config.participant_liveliness_assert_period.sec, 300);
    EXPECT_EQ(f_part_qos_2->discovery_config.initial_participant_announcements, 1);
    EXPECT_EQ(f_part_qos_2->discovery_config.min_initial_participant_announcement_period.sec, 1);
    EXPECT_EQ(f_part_qos_2->discovery_config.max_initial_participant_announcement_period.sec, 1);

    // publisher
    dal_publisher_t* pub = dal_domain_participant_create_publisher(
        participant, DAL_PUBLISHER_QOS_DEFAULT, NULL, DAL_ANY_STATUS);

    dal_publisher_t* pub_2 = dal_domain_participant_create_publisher_with_profile(
        participant, DAL_PUBLISHER_QOS_DEFAULT, "publisher_qos_aaa", NULL, DAL_ANY_STATUS);

    auto pub_impl = reinterpret_cast<DalPublisherImpl*>(pub->handle_);
    auto r_pub = pub_impl->RtiddsPublisher();
    auto pub_impl_2 = reinterpret_cast<DalPublisherImpl*>(pub_2->handle_);
    auto r_pub_2 = pub_impl_2->RtiddsPublisher();

    DDS_PublisherQos* r_pub_qos = new DDS_PublisherQos();
    DDS_PublisherQos* r_pub_qos_2 = new DDS_PublisherQos();
    DDS_Publisher_get_qos(r_pub, r_pub_qos);
    DDS_Publisher_get_qos(r_pub_2, r_pub_qos_2);

    EXPECT_EQ(r_pub_qos->presentation.access_scope, DDS_INSTANCE_PRESENTATION_QOS);
    EXPECT_EQ(r_pub_qos->presentation.coherent_access, false);
    EXPECT_EQ(r_pub_qos->presentation.ordered_access, false);

    EXPECT_STREQ(r_pub_qos_2->publisher_name.name, "publisher_AAA");
    EXPECT_EQ(r_pub_qos_2->presentation.access_scope, DDS_GROUP_PRESENTATION_QOS);
    EXPECT_EQ(r_pub_qos_2->presentation.coherent_access, true);
    EXPECT_EQ(r_pub_qos_2->presentation.ordered_access, true);

    // subscriber
    dal_subscriber_t* sub = dal_domain_participant_create_subscriber(
        participant, DAL_SUBSCRIBER_QOS_DEFAULT, NULL, DAL_ANY_STATUS);

    dal_subscriber_t* sub_2 = dal_domain_participant_create_subscriber_with_profile(
        participant, DAL_SUBSCRIBER_QOS_DEFAULT, "subscriber_qos_aaa", NULL, DAL_ANY_STATUS);

    auto sub_impl = reinterpret_cast<DalSubscriberImpl*>(sub->handle_);
    auto r_sub = sub_impl->RtiddsSubscriber();
    auto sub_impl_2 = reinterpret_cast<DalSubscriberImpl*>(sub_2->handle_);
    auto r_sub_2 = sub_impl_2->RtiddsSubscriber();

    DDS_SubscriberQos* r_qos_sub = new DDS_SubscriberQos();
    DDS_SubscriberQos* r_qos_sub_2 = new DDS_SubscriberQos();
    DDS_Subscriber_get_qos(r_sub, r_qos_sub);
    DDS_Subscriber_get_qos(r_sub_2, r_qos_sub_2);

    EXPECT_EQ(r_qos_sub->presentation.access_scope, DDS_INSTANCE_PRESENTATION_QOS);
    EXPECT_EQ(r_qos_sub->presentation.coherent_access, false);
    EXPECT_EQ(r_qos_sub->presentation.ordered_access, false);

    EXPECT_STREQ(r_qos_sub_2->subscriber_name.name, "subscriber_AAA");
    EXPECT_EQ(r_qos_sub_2->presentation.access_scope, DDS_GROUP_PRESENTATION_QOS);
    EXPECT_EQ(r_qos_sub_2->presentation.coherent_access, true);
    EXPECT_EQ(r_qos_sub_2->presentation.ordered_access, true);

    dal_type_support_t* ts =
        dal_create_type_support((void*)(mj_interfaces::msg::Mjtype::FullyQualifiedName()));
    ret = dal_type_support_register_type(ts, participant, dal_type_support_get_type_name(ts));

    dal_topic_t* topic = dal_domain_participant_create_topic(participant,
                                                             "HelloWorldTopic",
                                                             dal_type_support_get_type_name(ts),
                                                             DAL_TOPIC_QOS_DEFAULT,
                                                             NULL,
                                                             DAL_ANY_STATUS);
    // writer
    dal_datawriter_qos_t* d_writer_qos = dal_create_default_datawriter_qos();
    EXPECT_EQ(d_writer_qos->durability.kind, DAL_TRANSIENT_LOCAL_DURABILITY_QOS);
    EXPECT_EQ(d_writer_qos->reliability.kind, DAL_RELIABLE_RELIABILITY_QOS);

    d_writer_qos->durability.kind = DAL_TRANSIENT_DURABILITY_QOS;
    d_writer_qos->reliability.kind = DAL_BEST_EFFORT_RELIABILITY_QOS;

    setenv("NDDS_USER_QOS_SET_FLAG", "0", 1);
    dal_datawriter_t* writer =
        dal_publisher_create_datawriter(pub, topic, d_writer_qos, NULL, DAL_ANY_STATUS);
    EXPECT_NE(writer->handle_, nullptr);

    auto writer_impl = reinterpret_cast<DalDataWriterImpl*>(writer->handle_);
    auto r_writer = writer_impl->RtiddsDataWriter();
    DDS_DataWriterQos* r_writer_qos = new DDS_DataWriterQos();
    EXPECT_EQ(DDS_DataWriter_get_qos(r_writer, r_writer_qos), DDS_RETCODE_OK);

    EXPECT_EQ(r_writer_qos->durability.kind, DDS_TRANSIENT_DURABILITY_QOS);
    EXPECT_EQ(r_writer_qos->reliability.kind, DDS_BEST_EFFORT_RELIABILITY_QOS);
    EXPECT_EQ(r_writer_qos->history.kind, DDS_KEEP_LAST_HISTORY_QOS);
    EXPECT_EQ(r_writer_qos->liveliness.kind, DDS_AUTOMATIC_LIVELINESS_QOS);

    dal_datawriter_t* writer_2 = dal_publisher_create_datawriter_with_profile(
        pub, topic, DAL_DATAWRITER_QOS_DEFAULT, "datawriter_qos_aaa", NULL, DAL_ANY_STATUS);
    EXPECT_NE(writer_2->handle_, nullptr);

    auto writer_impl_2 = reinterpret_cast<DalDataWriterImpl*>(writer_2->handle_);
    auto r_writer_2 = writer_impl_2->RtiddsDataWriter();

    DDS_DataWriterQos* r_writer_qos_2 = new DDS_DataWriterQos();
    EXPECT_EQ(DDS_DataWriter_get_qos(r_writer_2, r_writer_qos_2), DDS_RETCODE_OK);

    EXPECT_EQ(r_writer_qos_2->durability.kind, DDS_VOLATILE_DURABILITY_QOS);
    EXPECT_EQ(r_writer_qos_2->reliability.kind, DDS_BEST_EFFORT_RELIABILITY_QOS);
    EXPECT_EQ(r_writer_qos_2->history.kind, DDS_KEEP_LAST_HISTORY_QOS);
    EXPECT_EQ(r_writer_qos_2->liveliness.kind, DDS_AUTOMATIC_LIVELINESS_QOS);

    // reader
    setenv("NDDS_USER_QOS_SET_FLAG", "0", 1);
    dal_datareader_t* reader = dal_subscriber_create_datareader(
        sub, topic, DAL_DATAREADER_QOS_DEFAULT, NULL, DAL_ANY_STATUS);
    EXPECT_NE(reader->handle_, nullptr);

    auto reader_impl = reinterpret_cast<DalDataReaderImpl*>(reader->handle_);
    auto f_reader = reader_impl->RtiDataReader();

    DDS_DataReaderQos* r_reader_qos = new DDS_DataReaderQos();
    EXPECT_EQ(DDS_DataReader_get_qos(f_reader, r_reader_qos), DDS_RETCODE_OK);

    EXPECT_EQ(r_reader_qos->durability.kind, DDS_VOLATILE_DURABILITY_QOS);
    EXPECT_EQ(r_reader_qos->reliability.kind, DDS_BEST_EFFORT_RELIABILITY_QOS);

    dal_datareader_t* reader_2 = dal_subscriber_create_datareader_with_profile(
        sub, topic, DAL_DATAREADER_QOS_DEFAULT, "datareader_qos_aaa", NULL, DAL_ANY_STATUS);
    EXPECT_NE(reader_2->handle_, nullptr);

    auto reader_impl_2 = reinterpret_cast<DalDataReaderImpl*>(reader_2->handle_);
    auto f_reader_2 = reader_impl_2->RtiDataReader();

    DDS_DataReaderQos* r_reader_qos_2 = new DDS_DataReaderQos();
    EXPECT_EQ(DDS_DataReader_get_qos(f_reader_2, r_reader_qos_2), DDS_RETCODE_OK);

    EXPECT_EQ(r_reader_qos_2->durability.kind, DDS_TRANSIENT_LOCAL_DURABILITY_QOS);
    EXPECT_EQ(r_reader_qos_2->reliability.kind, DDS_RELIABLE_RELIABILITY_QOS);
}

TEST(config_loader_api_takes_effect, config_loader)
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    setenv("NDDS_USER_QOS_SET_FLAG", "0", 1);
    setenv("DAL_CONFIG_PATH", "./DAL_RTI_USER_QOS_PROFILES_2.xml", 1);
    ret = dal_load_config_from_path("./DAL_RTI_USER_QOS_PROFILES.xml", nullptr);
    EXPECT_EQ(ret, DAL_RETCODE_OK);

    test_config_loader;
}

TEST(config_loader_env_takes_effect, config_loader)
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    setenv("NDDS_USER_QOS_SET_FLAG", "0", 1);
    setenv("DAL_CONFIG_PATH", "./DAL_RTI_USER_QOS_PROFILES.xml", 1);
    ret = dal_load_config_from_path("./DAL_RTI_USER_QOS_PROFILES_wrong.xml", nullptr);
    EXPECT_EQ(ret, DAL_RETCODE_OK);

    test_config_loader;
}
